package ace.module.account.core.impl.dao.aggregate.impl;

import ace.cmp.core.constants.CoreConstant;
import ace.module.account.api.enums.IamAccountPassportPassportTypeEnum;
import ace.module.account.api.model.input.IamAccountSearchInput;
import ace.module.account.core.impl.dao.aggregate.IamAccountAggregate;
import ace.module.account.core.impl.dao.entity.IamAccount;
import ace.module.account.core.impl.dao.entity.IamAccountEventRecord;
import ace.module.account.core.impl.dao.entity.IamAccountPassport;
import ace.module.account.core.impl.dao.entity.IamAccountPassword;
import ace.module.account.core.impl.dao.entity.IamAccountProfile;
import ace.module.account.core.impl.dao.entity.QIamAccount;
import ace.module.account.core.impl.dao.entity.QIamAccountPassport;
import ace.module.account.core.impl.dao.entity.QIamAccountProfile;
import ace.module.account.core.impl.dao.entity.QIamAccountTag;
import ace.module.account.core.impl.dao.repository.IamAccountEventRecordRepository;
import ace.module.account.core.impl.dao.repository.IamAccountPassportRepository;
import ace.module.account.core.impl.dao.repository.IamAccountPasswordRepository;
import ace.module.account.core.impl.dao.repository.IamAccountProfileRepository;
import ace.module.account.core.impl.dao.repository.IamAccountRepository;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Predicate;
import com.querydsl.sql.SQLExpressions;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

/**
 * 账户 aggregate implement.
 *
 * @author Caspar
 */
@Slf4j
@AllArgsConstructor
@Component
public class IamAccountAggregateImpl implements IamAccountAggregate {

  private final IamAccountRepository iamAccountRepository;
  private final IamAccountPasswordRepository iamAccountPasswordRepository;
  private final IamAccountPassportRepository iamAccountPassportRepository;
  private final IamAccountEventRecordRepository iamAccountEventRecordRepository;
  private final IamAccountProfileRepository iamAccountProfileRepository;

  @Override
  public Page<IamAccount> search(IamAccountSearchInput input, Pageable pageable) {
    QIamAccount qiamAccount = new QIamAccount("account");
    QIamAccountProfile qiamAccountProfile = new QIamAccountProfile("profile");
    QIamAccountPassport qiamAccountPassport = new QIamAccountPassport("passport");
    QIamAccountTag qiamAccountTag = new QIamAccountTag("account_tag");

    List<Predicate> predicates = new ArrayList<>();

    predicates.add(qiamAccount.iamAppId.eq(input.getIamAppId()));

    if (input.getFilter() != null) {
      if (input.getFilter().getAccountId() != null) {
        predicates.add(qiamAccount.id.eq(input.getFilter().getAccountId()));
      }
      if (StringUtils.isNotBlank(input.getFilter().getNickName())) {
        predicates.add(
            qiamAccount.id.in(
                SQLExpressions
                    .select(qiamAccountProfile.iamAccountId)
                    .from(qiamAccountProfile)
                    .where(
                        ExpressionUtils.allOf(
                            qiamAccountProfile.nickName.like(
                                "%" + input.getFilter().getNickName() + "%")
                        )
                    )
            )
        );
      }
      if (StringUtils.isNotBlank(input.getFilter().getPhone())) {
        predicates.add(
            qiamAccount.id.eq(
                SQLExpressions.select(qiamAccountPassport.iamAccountId)
                    .from(qiamAccountPassport)
                    .where(
                        ExpressionUtils.allOf(
                            qiamAccountPassport.iamAppId.eq(input.getIamAppId()),

                            qiamAccountPassport.passportType.eq(
                                IamAccountPassportPassportTypeEnum.PHONE.getCode()
                            ),

                            qiamAccountPassport.passport.eq(input.getFilter().getPhone()),

                            qiamAccountPassport.deleteFlag.eq(
                                CoreConstant.DELETE_FLAG_NOT
                            )
                        )
                    )
                    .limit(1)
            )
        );
      }
      if (CollectionUtils.isNotEmpty(input.getFilter().getAccountTags())) {
        List<Predicate> iamAccountTagKeyPredicateAll = new ArrayList<>();
        input.getFilter()
            .getAccountTags()
            .forEach(
                accountTagInput -> {
                  Predicate iamAccountTagKeyPredicate = null;
                  if (accountTagInput.getKey() != null) {
                    iamAccountTagKeyPredicate =
                        ExpressionUtils.and(
                            iamAccountTagKeyPredicate,
                            qiamAccountTag.key.eq(
                                accountTagInput.getKey()));
                  }
                  if (accountTagInput.getType() != null) {
                    iamAccountTagKeyPredicate =
                        ExpressionUtils.and(
                            iamAccountTagKeyPredicate,
                            qiamAccountTag.type.eq(
                                accountTagInput.getType()));
                  }
                  if (accountTagInput.getValue() != null) {
                    iamAccountTagKeyPredicate =
                        ExpressionUtils.and(
                            iamAccountTagKeyPredicate,
                            qiamAccountTag.value.eq(
                                accountTagInput.getValue()));
                  }
                  iamAccountTagKeyPredicateAll.add(iamAccountTagKeyPredicate);
                });
        predicates.add(
            qiamAccount.id.in(
                SQLExpressions
                    .select(qiamAccountTag.iamAccountId)
                    .from(qiamAccountTag)
                    .where(ExpressionUtils.allOf(iamAccountTagKeyPredicateAll))
            )
        );
      }
    }
    return this.iamAccountRepository.page(qiamAccount, predicates, pageable);
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public void insertRegisterAccount(IamAccount account,
      IamAccountPassport iamAccountPassport,
      IamAccountProfile accountProfile,
      IamAccountEventRecord accountEvent,
      IamAccountPassword accountPassword
  ) {
    iamAccountRepository.insert(account, false);
    iamAccountPassportRepository.insert(iamAccountPassport, false);
    if (accountPassword != null) {
      iamAccountPasswordRepository.insert(accountPassword, false);
    }
    iamAccountProfileRepository.insert(accountProfile, false);
    iamAccountEventRecordRepository.insert(accountEvent, false);
  }
}